package com.ljx.recruitment.interview.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljx.recruitment.common.domain.vo.resp.PageResp;
import com.ljx.recruitment.common.enums.CommonStatusTypeEnum;
import com.ljx.recruitment.common.exception.ApiException;
import com.ljx.recruitment.interview.constant.JobConstant;
import com.ljx.recruitment.interview.domain.dto.DashboardDeliverTrendDateDto;
import com.ljx.recruitment.interview.domain.dto.JobProcessKeyAndBatchDto;
import com.ljx.recruitment.interview.domain.dto.TitleDetailsDto;
import com.ljx.recruitment.interview.domain.entity.Deliver;
import com.ljx.recruitment.interview.domain.entity.Job;
import com.ljx.recruitment.interview.domain.entity.JobProcess;
import com.ljx.recruitment.interview.domain.entity.JobType;
import com.ljx.recruitment.interview.domain.vo.req.DeliverPageReq;
import com.ljx.recruitment.interview.domain.vo.req.DeliverProcessReq;
import com.ljx.recruitment.interview.domain.vo.req.DeliverSubmitReq;
import com.ljx.recruitment.interview.domain.vo.resp.DeliverOptResp;
import com.ljx.recruitment.interview.domain.vo.resp.DeliverResp;
import com.ljx.recruitment.interview.enums.*;
import com.ljx.recruitment.interview.mapper.DeliverMapper;
import com.ljx.recruitment.interview.service.*;
import com.ljx.recruitment.interview.service.impl.process.*;
import com.ljx.recruitment.security.util.UserUtil;
import com.ljx.recruitment.user.cache.UserCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: LiJunXi
 * @Description:
 * @Date: 2024-11-02  00:06
 */
@Service
public class DeliverServiceImpl extends ServiceImpl<DeliverMapper, Deliver> implements DeliverService {

    @Autowired
    private DeliverMapper deliverMapper;

    @Autowired
    private JobTypeService jobTypeService;

    @Autowired
    private ResumeService resumeService;

    @Autowired
    private JobProcessService jobProcessService;

    @Autowired
    private UserCache userCache;

    @Override
    public PageResp<DeliverResp> getPage(DeliverPageReq req) {
        req.setUserId(UserUtil.getUserId());
        req.setCurrPage((req.getCurrPage() - 1) * req.getPageSize());
        req.setIsAdmin(UserUtil.checkIsTenantAdmin() || UserUtil.checkIsPlatformAdmin() || UserUtil.checkIsHr());
        req.setTenantId(UserUtil.tryToGetTenantId());
        List<DeliverResp> respList = deliverMapper.getPage(req);
        Integer total = deliverMapper.getPageTotal(req);

        return PageResp.<DeliverResp>builder()
                .total(Long.parseLong(total.toString()))
                .list(fillDeliverUiParams(respList))
                .build();

    }

    private List<DeliverResp> fillDeliverUiParams(List<DeliverResp> respList) {
        if (CollectionUtil.isEmpty(respList)) {
            return new ArrayList<>();
        }
        List<Long> jobTypeIds = respList.stream().map(DeliverResp::getJobTypeId).collect(Collectors.toList());
        Map<Long, JobType> jobTypeMap = jobTypeService.listByIds(jobTypeIds).stream().collect(Collectors.toMap(JobType::getId, Function.identity()));
        List<Long> jobIds = respList.stream().map(DeliverResp::getJobId).collect(Collectors.toList());
        Map<Long, Map<Integer, List<JobProcess>>> JobsProcessMap = jobProcessService.getProcessMapByJobIds(jobIds);
        return respList.stream().map(item -> {
            item.setCategoryDesc(JobCategoryEnum.getByKey(item.getCategory()).getDesc());
            item.setKindDesc(JobKindEnum.getByKey(item.getKind()).getDesc());
            item.setJobTypeDesc(jobTypeMap.get(item.getJobTypeId()).getName());
            // 返回参数
            item.setOpts(getDeliverOptions(item));
            // 下一个流程状态
            item.setNextDeliverStatus(getNextDeliverStatusAndBatch(item.getId(), item.getJobId()).getJobProcessKey());
            // 流程是否结束
            if (DeliverStatusEnum.OFFER.getKey().equals(item.getDeliverStatus()) || DeliverStatusEnum.OVER.getKey().equals(item.getDeliverStatus())
                    || item.isRejectStage()) {
                item.setIsLastBatch(false);
                item.setDeliverStatusDesc(DeliverStatusEnum.cache.get(item.getDeliverStatus()).getValue());
                return item;
            }
            item.setDeliverStatusDesc(DeliverStatusEnum.cache.get(item.getDeliverStatus()).getValue() + item.getBatch());
            Map<Integer, List<JobProcess>> jobProcessMap = JobsProcessMap.get(item.getJobId());
            List<JobProcess> jobProcesses = jobProcessMap.get(item.getDeliverStatus());
            item.setIsLastBatch(!isHashNextProcess(jobProcessMap, item.getDeliverStatus()) && jobProcesses.size() == item.getBatch());
            return item;
        }).collect(Collectors.toList());

    }

    private DeliverOptResp getDeliverOptions(DeliverResp item) {
        DeliverOptResp deliverOptResp = new DeliverOptResp();
        deliverOptResp.setProcessAble(item.isRejectStage());
        deliverOptResp.setOfferAble(item.isOfferStage());
        deliverOptResp.setPassAble(item.isProcessIngStage() && !item.isOfferStage() && !item.isOverStage());
        deliverOptResp.setRejectAble(item.isProcessIngStage() && !item.isOverStage());
        return deliverOptResp;
    }

    private Boolean isHashNextProcess(Map<Integer, List<JobProcess>> jobProcessMap, Integer deliverStatus) {
        Boolean flag = false;
        for (Integer key : jobProcessMap.keySet()) {
            if (flag) return true;
            if (deliverStatus.equals(key)) flag = true;
        }
        return false;
    }

    @Override
    public void changeStatus(Long deliverId, Integer option, DeliverSubmitReq req) {
        if (Objects.isNull(deliverId) || Objects.isNull(option)) {
            throw new ApiException("参数不能为空");
        }
        Deliver deliver = deliverMapper.selectById(deliverId);
        checkChangeStatusParams(deliver.getDeliverStatus(), option);
        DeliverProcess deliverProcess = getDeliverProcess(deliver.getDeliverStatus());
        updateDeliverStatusAndFillTitleDetailsParams(deliver, req, option);
        deliverProcess.handleDeliver(deliver, req, option);
    }

    @Override
    public DeliverResp submit(DeliverSubmitReq req) {
        checkSubmitParams(req);
        Boolean isRecommend = resumeService.checkRecommend(req.getResumeDetails());
        JobProcessKeyAndBatchDto nextDeliverStatus = getNextDeliverStatusAndBatch(null, req.getJobId());
        Deliver deliver = BeanUtil.copyProperties(req, Deliver.class);
        deliver.setUId(UserUtil.getUserId());
        deliver.setDeliverStatus(nextDeliverStatus.getJobProcessKey());
        deliver.setIsRecommend(RecommendEnum.getByKey(isRecommend).getValue());
        deliver.setBatch(nextDeliverStatus.getBatch());
        if (isRecommend) {
            deliver.setRecommendCode((String) resumeService.getValueByCode(req.getResumeDetails(), "recommend code"));
        }
        deliverMapper.insert(deliver);
        return getInfo(deliver.getId());
    }

    private JobProcessKeyAndBatchDto getNextDeliverStatusAndBatch(Long deliverId, Long jobId) {
        List<JobProcess> jobProcesses = jobProcessService.getProcessByJobId(jobId);
        Map<Integer, List<JobProcess>> processMap = jobProcesses.stream().collect(Collectors.groupingBy(JobProcess::getProcessKey));
        Integer firstStatus = jobProcesses.get(0).getProcessKey();
        if (Objects.isNull(deliverId)) {
            // 初始化投递
            return new JobProcessKeyAndBatchDto(firstStatus, JobConstant.DEFAULT_PROCESS_BATCH);
        }
        Deliver deliver = deliverMapper.selectById(deliverId);
        if (Objects.isNull(deliver)) {
            // 初始化投递
            return new JobProcessKeyAndBatchDto(firstStatus, JobConstant.DEFAULT_PROCESS_BATCH);
        }
        if (DeliverStatusEnum.OFFER.getKey().equals(deliver.getDeliverStatus())) {
            return new JobProcessKeyAndBatchDto(JobBaseProcessEnum.OVER.getKey(), JobConstant.DEFAULT_PROCESS_BATCH);
        } else if (DeliverStatusEnum.OVER.getKey().equals(deliver.getDeliverStatus())) {
            return new JobProcessKeyAndBatchDto(null, null);
        } else if (deliver.isRejectStage()) {
            // 已被拒绝，流程结束
            return new JobProcessKeyAndBatchDto(null, null);
        }
        if (processMap.get(deliver.getDeliverStatus()).size() == deliver.getBatch()) {
            // 当前流程已完成， 进入下一个流程
            for (int i = 0; i < jobProcesses.size(); i++) {
                if (deliver.getDeliverStatus() < jobProcesses.get(i).getProcessKey()) {
                    // 找到第一个大于当前投递状态的值
                    return new JobProcessKeyAndBatchDto(jobProcesses.get(i).getProcessKey(), JobConstant.DEFAULT_PROCESS_BATCH);
                }
            }
        } else {
            // 当前流程未完成
            for (int i = 0; i < jobProcesses.size(); i++) {
                Boolean currentKeyAndBatch = deliver.getDeliverStatus().equals(jobProcesses.get(i).getProcessKey()) && deliver.getBatch().equals(jobProcesses.get(i).getBatch());
                Boolean hasNextBatch = i + 1 < jobProcesses.size() && deliver.getDeliverStatus().equals(jobProcesses.get(i + 1).getProcessKey());
                if (currentKeyAndBatch && hasNextBatch) {
                    // 找到当前流程批次，并且存在下一批次
                    return new JobProcessKeyAndBatchDto(deliver.getDeliverStatus(), jobProcesses.get(i + 1).getBatch());
                }
            }
        }
        // 进入谈薪阶段
        return new JobProcessKeyAndBatchDto(JobBaseProcessEnum.OFFER.getKey(), JobConstant.DEFAULT_PROCESS_BATCH);
    }

    private void checkSubmitParams(DeliverSubmitReq req) {
        if (Objects.isNull(req.getJobId())) {
            throw new ApiException("必要参数不能为空");
        }
        Integer count = deliverMapper.selectCount(new LambdaQueryWrapper<Deliver>()
                .eq(Deliver::getUId, UserUtil.getUserId())
                .eq(Deliver::getJobId, req.getJobId())
                .eq(Deliver::getStatus, CommonStatusTypeEnum.NORMAL.getKey()));
        if (count > 0) {
            throw new ApiException("请勿重复投递");
        }
    }

    @Override
    public DeliverResp getInfo(Long deliverId) {
        if (Objects.isNull(deliverId)) {
            throw new ApiException("ID不能为空");
        }
        Deliver deliver = deliverMapper.selectOne(new LambdaQueryWrapper<Deliver>()
                .eq(Deliver::getId, deliverId)
                .eq(Deliver::getStatus, CommonStatusTypeEnum.NORMAL.getKey()));
        return BeanUtil.copyProperties(deliver, DeliverResp.class);
    }

    private void updateDeliverStatusAndFillTitleDetailsParams(Deliver deliver, DeliverSubmitReq req, Integer option) {
        TitleDetailsDto titleDetailsDto = new TitleDetailsDto();
        Job job = SpringUtil.getBean(JobService.class).getById(deliver.getJobId());
        JobProcessKeyAndBatchDto nextDeliverStatus = getNextDeliverStatusAndBatch(deliver.getId(), job.getId());
        // 设置旧标题（邮件）
        if (DeliverStatusEnum.OFFER.getKey().equals(deliver.getDeliverStatus())) {
            // 如果是Offer阶段就不用带批次
            titleDetailsDto.setOldTitle(JobBaseProcessEnum.cache.get(deliver.getDeliverStatus()).getValue());
        } else {
            titleDetailsDto.setOldTitle(JobBaseProcessEnum.cache.get(deliver.getDeliverStatus()).getValue() + deliver.getBatch());
        }
        if (DeliverOptionEnum.REJECT.getKey().equals(option)) {
            // 拒绝
            DeliverStatusEnum deliverStatusEnum = DeliverStatusEnum.cache.get(deliver.getDeliverStatus() + option);
            deliver.setDeliverStatus(deliverStatusEnum.getKey());
            deliver.setBatch(JobConstant.DEFAULT_PROCESS_BATCH);
        } else {
            deliver.setDeliverStatus(nextDeliverStatus.getJobProcessKey());
            deliver.setBatch(nextDeliverStatus.getBatch());
            // 设置新标题（邮件）
            if (DeliverStatusEnum.OFFER.getKey().equals(deliver.getDeliverStatus())) {
                // 如果是发Offer就不用带批次
                titleDetailsDto.setNewTitle(JobBaseProcessEnum.cache.get(deliver.getDeliverStatus()).getValue());
            } else {
                titleDetailsDto.setNewTitle(JobBaseProcessEnum.cache.get(deliver.getDeliverStatus()).getValue() + deliver.getBatch());
            }
        }
        req.setTitleDetails(titleDetailsDto);
    }

    private void checkChangeStatusParams(Integer status, Integer option) {
        if (!DeliverOptionEnum.PASS.getKey().equals(option) && !DeliverOptionEnum.REJECT.getKey().equals(option)) {
            throw new ApiException("操作非法");
        }
        if (DeliverStatusEnum.OVER.getKey().equals(status)) {
            throw new ApiException("流程已结束");
        }
    }

    private DeliverProcess getDeliverProcess(Integer status) {
        if (status.equals(DeliverStatusEnum.RESUME_SCREENING.getKey())) {
            return SpringUtil.getBean(ResumeScreeningProcess.class);
        } else if (status.equals(+DeliverStatusEnum.WRITTEN_EXAMINATION.getKey())) {
            return SpringUtil.getBean(WrittenExaminationProcess.class);
        } else if (status.equals(DeliverStatusEnum.MAJOR_INTERVIEW.getKey())) {
            return SpringUtil.getBean(MajorInterviewProcess.class);
        } else if (status.equals(DeliverStatusEnum.HR_INTERVIEW.getKey())) {
            return SpringUtil.getBean(HrInterviewProcess.class);
        } else if (status.equals(DeliverStatusEnum.OFFER.getKey())) {
            return SpringUtil.getBean(OfferProcess.class);
        } else {
            throw new ApiException("流程已结束");
        }
    }


    @Override
    public void processSubmit(DeliverProcessReq req) {
        checkProcessSubmitParams(req);
        Deliver deliver = deliverMapper.selectById(req.getDeliverId());
        deliver.setDeliverStatus(req.getJobProcessKey());
        deliver.setBatch(req.getBatch());
        deliverMapper.updateById(deliver);
        sendSalvageEmail(deliver);
    }

    private void sendSalvageEmail(Deliver deliver) {
        if (Objects.isNull(deliver)) {
            throw new ApiException("重要参数不能为空");
        }
        DeliverProcess deliverProcess = SpringUtil.getBean(SalvageProcess.class);
        deliverProcess.handleDeliver(deliver, null, null);
    }

    private void checkProcessSubmitParams(DeliverProcessReq req) {
        if (!UserUtil.checkIsTenantAdmin()) {
            throw new ApiException("无权限操作");
        }
        if (Objects.isNull(req.getDeliverId()) || Objects.isNull(req.getJobProcessKey())) {
            throw new ApiException("重要参数不能为空");
        }
        boolean isExist = false;
        for (DeliverStatusEnum value : DeliverStatusEnum.values()) {
            if (value.getKey().equals(req.getJobProcessKey())) {
                isExist = true;
                break;
            }
        }
        if (!isExist) {
            throw new ApiException("状态不存在");
        }
        if (req.getJobProcessKey() % 2 == 0) {
            throw new ApiException("此状态无法设置");
        }
        Deliver deliver = deliverMapper.selectById(req.getDeliverId());
        if (Objects.isNull(deliver)) {
            throw new ApiException("投递不存在");
        }
    }

    @Override
    public List<JobProcess> getDeliverProcessSettingList(Long jobId, Long deliverId) {
        Deliver deliver = deliverMapper.selectById(deliverId);
        return jobProcessService.getProcessByJobId(jobId)
                .stream().filter(item -> item.getProcessKey() <= deliver.getDeliverStatus() && item.getProcessKey() % 2 == 1)
                .collect(Collectors.toList());
    }

    @Override
    public PageResp<DeliverResp> getRecommendDeliverList(DeliverPageReq req) {
        req.setCurrPage((req.getCurrPage() - 1) * req.getPageSize());
        req.setTenantId(UserUtil.tryToGetTenantId());
        req.setIsAdmin(true);
        req.setRecommendCode(userCache.get(UserUtil.getUserId()).getRecommendCode());
        List<DeliverResp> respList = deliverMapper.getPage(req);
        Integer total = deliverMapper.getPageTotal(req);

        return PageResp.<DeliverResp>builder()
                .total(Long.parseLong(total.toString()))
                .list(fillDeliverUiParams(respList))
                .build();
    }

    @Override
    public Boolean checkIsDeliver(Long jobId) {
        if (Objects.isNull(jobId)) {
            throw new ApiException("必要参数不能为空");
        }
        Integer count = deliverMapper.selectCount(new LambdaQueryWrapper<Deliver>()
                .eq(Deliver::getUId, UserUtil.getUserId())
                .eq(Deliver::getJobId, jobId)
                .eq(Deliver::getStatus, CommonStatusTypeEnum.NORMAL.getKey()));
        if (count > 0) {
            return true;
        }
        return false;
    }

    @Override
    public Integer getTenantDeliverCount(Long tenantId) {
        if (Objects.isNull(tenantId)) {
            throw new ApiException("租户ID为空");
        }
        return deliverMapper.getTenantDeliver(tenantId).size();
    }

    @Override
    public Integer getDeliverCompletionRate() {
        List<Deliver> deliverList = deliverMapper.getTenantDeliver(UserUtil.tryToGetTenantId());
        List<Deliver> deliverOverList = deliverList.stream().filter(
                item -> DeliverStatusEnum.OVER.getKey().equals(item.getDeliverStatus())
        ).collect(Collectors.toList());

        BigDecimal overSize = new BigDecimal(deliverOverList.size());
        BigDecimal totalSize = new BigDecimal(deliverList.size());
        if (totalSize.compareTo(BigDecimal.ZERO) == 0) {
            return 0;
        }
        BigDecimal completionRate = overSize.divide(totalSize, 2, RoundingMode.HALF_UP)
                                            .multiply(new BigDecimal(100));
        return completionRate.intValue();
    }

    @Override
    public List<DashboardDeliverTrendDateDto> getTenantCountGroupByDate(Long tenantId, List<Date> dates) {
        if (Objects.isNull(tenantId)) {
            throw new ApiException("租户ID为空");
        }
        if (CollectionUtil.isEmpty(dates)) {
            return new ArrayList<>();
        }
        return deliverMapper.getCountGroupByDate(tenantId, dates);
    }
}
